1 From fda1e0af7c28f96d4f33e57cf51565b0e9c14e63 Mon Sep 17 00:00:00 2001
2 From: Sebastian Reichel <sebastian.reichel@collabora.com>
3 Date: Fri, 31 Oct 2025 16:58:23 +0100
4 Subject: [PATCH] mmc: sdhci-of-dwcmshc: Add command queue support for rockchip
7 This adds CQE support for the Rockchip RK3588 and RK3576 platform. To
8 be functional, the eMMC device-tree node must have a 'supports-cqe;'
11 As the RK3576 device-tree has been upstreamed with the 'supports-cqe;'
12 property set by default, the kernel already tried to use CQE, which
13 results in system hang during suspend. This fixes the issue.
15 Co-developed-by: Yifeng Zhao <yifeng.zhao@rock-chips.com>
16 Signed-off-by: Yifeng Zhao <yifeng.zhao@rock-chips.com>
17 Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
18 Acked-by: Adrian Hunter <adrian.hunter@intel.com>
19 Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
21 drivers/mmc/host/sdhci-of-dwcmshc.c | 93 ++++++++++++++++++++++++++++-
22 1 file changed, 90 insertions(+), 3 deletions(-)
24 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c
25 +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
28 #include "sdhci-pltfm.h"
30 +#include "sdhci-cqhci.h"
32 #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
35 #define DWCMSHC_EMMC_DLL_TXCLK 0x808
36 #define DWCMSHC_EMMC_DLL_STRBIN 0x80c
37 #define DECMSHC_EMMC_DLL_CMDOUT 0x810
38 +#define DECMSHC_EMMC_MISC_CON 0x81C
39 +#define MISC_INTCLK_EN BIT(1)
40 #define DWCMSHC_EMMC_DLL_STATUS0 0x840
41 #define DWCMSHC_EMMC_DLL_START BIT(0)
42 #define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
43 @@ -234,6 +237,7 @@ struct dwcmshc_priv {
45 struct dwcmshc_pltfm_data {
46 const struct sdhci_pltfm_data pdata;
47 + const struct cqhci_host_ops *cqhci_host_ops;
48 int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
49 void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
51 @@ -603,6 +607,68 @@ static void dwcmshc_cqhci_dumpregs(struc
52 sdhci_dumpregs(mmc_priv(mmc));
55 +static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc)
57 + struct sdhci_host *host = mmc_priv(mmc);
58 + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
59 + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
62 + reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
63 + reg |= CQHCI_ENABLE;
64 + sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
67 +static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc)
69 + struct sdhci_host *host = mmc_priv(mmc);
72 + reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
73 + while (reg & SDHCI_DATA_AVAILABLE) {
74 + sdhci_readl(host, SDHCI_BUFFER);
75 + reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
78 + sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
80 + sdhci_cqe_enable(mmc);
83 +static void rk35xx_sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
85 + struct sdhci_host *host = mmc_priv(mmc);
86 + unsigned long flags;
90 + * During CQE command transfers, command complete bit gets latched.
91 + * So s/w should clear command complete interrupt status when CQE is
92 + * either halted or disabled. Otherwise unexpected SDCHI legacy
93 + * interrupt gets triggered when CQE is halted/disabled.
95 + spin_lock_irqsave(&host->lock, flags);
96 + ctrl = sdhci_readl(host, SDHCI_INT_ENABLE);
97 + ctrl |= SDHCI_INT_RESPONSE;
98 + sdhci_writel(host, ctrl, SDHCI_INT_ENABLE);
99 + sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
100 + spin_unlock_irqrestore(&host->lock, flags);
102 + sdhci_cqe_disable(mmc, recovery);
105 +static void rk35xx_sdhci_cqe_post_disable(struct mmc_host *mmc)
107 + struct sdhci_host *host = mmc_priv(mmc);
108 + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
109 + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
112 + ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
113 + ctrl &= ~CQHCI_ENABLE;
114 + sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
117 static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
119 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
120 @@ -721,6 +787,10 @@ static void rk35xx_sdhci_reset(struct sd
121 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
122 struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
123 struct rk35xx_priv *priv = dwc_priv->priv;
124 + u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON);
126 + if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL))
127 + cqhci_deactivate(host->mmc);
129 if (mask & SDHCI_RESET_ALL && priv->reset) {
130 reset_control_assert(priv->reset);
131 @@ -729,6 +799,9 @@ static void rk35xx_sdhci_reset(struct sd
134 sdhci_reset(host, mask);
136 + /* Enable INTERNAL CLOCK */
137 + sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON);
140 static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
141 @@ -1230,6 +1303,15 @@ static const struct dwcmshc_pltfm_data s
145 +static const struct cqhci_host_ops rk35xx_cqhci_ops = {
146 + .pre_enable = rk35xx_sdhci_cqe_pre_enable,
147 + .enable = rk35xx_sdhci_cqe_enable,
148 + .disable = rk35xx_sdhci_cqe_disable,
149 + .post_disable = rk35xx_sdhci_cqe_post_disable,
150 + .dumpregs = dwcmshc_cqhci_dumpregs,
151 + .set_tran_desc = dwcmshc_set_tran_desc,
154 static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
156 .ops = &sdhci_dwcmshc_rk35xx_ops,
157 @@ -1238,6 +1320,7 @@ static const struct dwcmshc_pltfm_data s
158 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
159 SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
161 + .cqhci_host_ops = &rk35xx_cqhci_ops,
162 .init = dwcmshc_rk35xx_init,
163 .postinit = dwcmshc_rk35xx_postinit,
165 @@ -1287,7 +1370,8 @@ static const struct cqhci_host_ops dwcms
166 .set_tran_desc = dwcmshc_set_tran_desc,
169 -static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev)
170 +static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev,
171 + const struct dwcmshc_pltfm_data *pltfm_data)
173 struct cqhci_host *cq_host;
174 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
175 @@ -1317,7 +1401,10 @@ static void dwcmshc_cqhci_init(struct sd
178 cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
179 - cq_host->ops = &dwcmshc_cqhci_ops;
180 + if (pltfm_data->cqhci_host_ops)
181 + cq_host->ops = pltfm_data->cqhci_host_ops;
183 + cq_host->ops = &dwcmshc_cqhci_ops;
185 /* Enable using of 128-bit task descriptors */
186 dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
187 @@ -1486,7 +1573,7 @@ static int dwcmshc_probe(struct platform
188 priv->vendor_specific_area2 =
189 sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
191 - dwcmshc_cqhci_init(host, pdev);
192 + dwcmshc_cqhci_init(host, pdev, pltfm_data);
195 if (pltfm_data->postinit)